//
//  IMTCANR0MessageCoder.m
//  INMOTION
//
//  Created by gzc on 2018/10/16.
//  Copyright © 2018年 Inmotion. All rights reserved.
//
/// r0的组包与解包处理

#import "IMTCANR0MessageCoder.h"

@implementation IMTCANR0MessageCoder

#pragma mark - =======封包=========
// 通信加密值
unsigned char gRandCryptKey = 0;    // 密钥索引值,必须是U8类型,注意：需要将此变量进行随机处理

const unsigned char PackCryptTab[256] = {
    144,136,240,229, 79,231,110, 69,    215,238,101,120,239,153,163, 82,        127, 90,148,247,246,205,171,162,         24, 27,250,142,253,  6,113, 68,
    98, 86,243,220,201,200,222,248,        203, 32,177, 43,132,146,202, 37,         60,130,172,157,121,181, 88, 85,        105, 44,251,252,235,  1, 76,198,
    34,180, 91, 20,193, 92,213,187,        145,176, 38,219, 87,182,103,216,         84,223,126, 40,233,167,  9,218,        134, 46, 15, 61,  3, 51, 63,112,
    131, 16, 10, 30,190, 94,161, 35,    191,109,115,125,122,114,192,102,          2, 39, 57,209,225, 25,129,111,         81,169, 26, 75,230,155,212,139,
    78,158,116, 54, 71,185,186, 11,         18,106,149,236,227,204, 22,242,          4,108,107, 96,226,245, 70, 33,         21,138,164,104, 52,150, 58, 49,
    140,151, 89, 67,210, 64, 74,194,    241,199,166,156, 80,255,228,188,         12,118, 45,159, 56,207,141,  0,        128, 13, 73,197,133, 95, 14,173,
    31,175, 97,179,249,124,165,244,        189, 41,  5,117,154,  7,184,206,        232,137,183,  8, 36,234,178,100,         19,254, 50,152,119,143, 93, 29,
    59, 83,211,224, 65,195, 28,196,        174,221,208,147, 55,237,135,168,        170, 17,160,123, 72, 77, 23, 48,        217, 47,214, 99, 53, 42, 62, 66,
};

/**
 向R0发送指令
 
 @param messageID 要读取对象的索引
 @param length 要读取的长度
 @param isRead YES:读命令 NO：写命令
 @return 返回打包后的Data 10000 data 10000 2
 */
+ (NSData *)dataWithMessageID:(u_int32_t)messageID length:(unsigned char)length isRead:(BOOL)isRead {
    
    Byte *bytes = (Byte *)malloc(13);
    unsigned char ID = messageID;   /// 要读取对象的索引
    unsigned char Data = length;    /// 要读取的长度
    unsigned char SLen = MakeCommPack_WriteCache(bytes, 13, DEV_MAIN_CTRL, isRead ? CMD_READ : CMD_WRITE, ID, &Data, 1);
    NSData *encodedR0Data = [NSData dataWithBytes:bytes length:SLen];
    
    return encodedR0Data;
}

+ (NSData *)dataWithMessageID:(u_int32_t)messageID withData:(unsigned char *)data length:(unsigned char)length{
    Byte *bytes = (Byte *)malloc(13);
    unsigned char ID = messageID;// 要读取对象的索引
//    NSData *testdata = [[NSData alloc] initWithBytes:data length:length];  没有用到
    
    unsigned char SLen = MakeCommPack_WriteCache(bytes, 13, DEV_MAIN_CTRL, CMD_WRITE, ID, data, length);
    NSData *encodedR0Data = [NSData dataWithBytes:bytes length:SLen];
    
    return encodedR0Data;
}

+ (NSData *)dataWithIAPCMD:(unsigned char)cmdHeader MessageID:(u_int32_t)messageID withData:(unsigned char *)data length:(unsigned char)length{
    Byte *bytes = (Byte *)malloc(length + 9);
    unsigned char ID = messageID;// 要读取对象的索引
//    NSData *testdata = [[NSData alloc] initWithBytes:data length:length];  没有用到
    
    /*
     length = 4
     05000400
     55ab06f7 3adc2fd3 63312ad4 bc
     05000400 05000400
     55ab0af7 3adc2fd3 63312a3b 4294882d 1b
     */
    /// DEV_MAIN_BLE 蓝牙版。 DEV_MAIN_CTRL主控板
    unsigned char SLen = MakeCommPack_WriteCache(bytes, length + 9, DEV_MAIN_CTRL, cmdHeader, ID, data, length);
    NSData *encodedR0Data = [NSData dataWithBytes:bytes length:SLen];
    
    return encodedR0Data;
}
+ (NSData *)dataToDeviceWithIAPCMD:(unsigned char)cmdHeader MessageID:(u_int32_t)messageID withData:(unsigned char *)data length:(unsigned char)length toDevice:(BOOL)isble{
    Byte *bytes = (Byte *)malloc(length + 9);
    unsigned char ID = messageID;// 要读取对象的索引
    //    NSData *testdata = [[NSData alloc] initWithBytes:data length:length];  没有用到
    
    /*
     length = 4
     05000400
     55ab06f7 3adc2fd3 63312ad4 bc
     05000400 05000400
     55ab0af7 3adc2fd3 63312a3b 4294882d 1b
     */
    unsigned char SLen;     /// DEV_MAIN_BLE 蓝牙版。 DEV_MAIN_CTRL主控板
    if (isble) {
        SLen = MakeCommPack_WriteCache(bytes, length + 9, DEV_MAIN_BLE, cmdHeader, ID, data, length);
    }else {
       SLen = MakeCommPack_WriteCache(bytes, length + 9, DEV_MAIN_CTRL, cmdHeader, ID, data, length);
    }
    NSData *encodedR0Data = [NSData dataWithBytes:bytes length:SLen];
    
    return encodedR0Data;
}


+ (NSData *)dataWithIAPCMD:(unsigned char)cmdHeader toDevice:(BOOL )isBle MessageID:(u_int32_t)messageID withData:(unsigned char *)data length:(unsigned char)length{
    Byte *bytes = (Byte *)malloc(length + 9);
    unsigned char ID = messageID;// 要读取对象的索引
    //    NSData *testdata = [[NSData alloc] initWithBytes:data length:length];  没有用到
    
    /*
     length = 4
     05000400
     55ab06f7 3adc2fd3 63312ad4 bc
     05000400 05000400
     55ab0af7 3adc2fd3 63312a3b 4294882d 1b
     */
    /// DEV_MAIN_BLE 蓝牙版。 DEV_MAIN_CTRL主控板
    unsigned char SLen ;
    if (isBle) {
        SLen = MakeCommPack_WriteCache(bytes, length + 9, DEV_MAIN_BLE, cmdHeader, ID, data, length);
        NSLog(@"💩💩💩💩 ------- 更新蓝牙板");
    }else {
        SLen = MakeCommPack_WriteCache(bytes, length + 9, DEV_MAIN_CTRL, cmdHeader, ID, data, length);
        NSLog(@"💩💩💩💩 ------- 更新主控板");

    }
    NSData *encodedR0Data = [NSData dataWithBytes:bytes length:SLen];
    
    return encodedR0Data;
}


/* 打包报文，并写入指定发送缓存
 pBuf:     报文发送缓冲区
 BufSize： 缓冲区可用大小
 
 dev :     指定将报文发送到哪里，0x0A 发送到主控，0x0D发送到APP 0x0b发送到蓝牙版
 cmd：     功能指令 ：读 写
 extra：    扩展功能码 （即：ID-要读取对象的索引）
 data：    数据段
 Len：    数据段长度
 */
unsigned char MakeCommPack_WriteCache(unsigned char* pBuf,
                                      u_int8_t BufSize,
                                      unsigned char dev,
                                      unsigned char cmd,
                                      unsigned char extra,
                                      unsigned char *data,
                                      int Len) {
    
    unsigned short checkCode = 0;
    int sendLen = 0;// 打包后的数据长度
    // 通信加密值 密钥索引值,必须是U8类型,注意：需要将此变量进行随机处理
    gRandCryptKey = rand() % 255; // 随机变更密钥,在此处或别处都行，保证变化就可以了
    
    /** randKey 假如是256的话 自动变为了0  */
    unsigned char randKey = gRandCryptKey;
    
    if (Len <= (BufSize - 9))// 固定占用9字节
    {
        pBuf[0] = 0x55;// 报头
        pBuf[1] = 0xAB;// 报头
        pBuf[2] = Len + 2;// 报文长度 = 描述fun + extra + data的长度
        pBuf[3] = randKey;// 加密
        pBuf[4] = dev ^ PackCryptTab[randKey++];// 指定将报文发送到哪里 密钥使用后，索引加1，下同
        pBuf[5] = cmd ^ PackCryptTab[randKey++];// 功能指令 读、写
        pBuf[6] = extra ^ PackCryptTab[randKey++];
        sendLen = 7;
        checkCode = pBuf[2] + pBuf[3] + dev + cmd + extra;
        
        // 数据段
        int i;
        for (i = 0; i < Len; i++)
        {
            pBuf[sendLen++] = (data[i] ^ PackCryptTab[randKey++]);
            checkCode += data[i];// 校验值计算的是明文累加和
        }
        checkCode = checkCode ^ 0xFFFF;
        
        // 校验低8
        pBuf[sendLen++] = (unsigned char)(checkCode & 0xFF)^ PackCryptTab[randKey++];// 最后校验值也一起加密
        pBuf[sendLen++] = (unsigned char)((checkCode >> 8) & 0xFF)^ PackCryptTab[randKey++];// 校验高8
        
        return sendLen;
    }
    
    return 0;
}

#pragma mark - =======解包=========
u_int32_t gDataMap[256];  /// 存放收到的车辆数据表，和对象一一对应。驱动层写入到这里，UI层从这里获取
S_Codec gCodecApp;

/**
 解包数据
 
 @param data 要解包的数据
 */
+ (void)unpackWithData:(NSData *)data resultBlock:(void (^)(NSData *data ,u_int8_t ID))resultBlock
{
    NSUInteger length = [data length];
    Byte *bytes = (Byte *) malloc(length);
    [data getBytes:bytes length:length];
    //    BleRecvedCallBack(bytes,length);
    NSLog(@"元数据---%@",data);
    
    for(int i=0; i < length; i++)
    {
        if(SeqCommPack(&gCodecApp,bytes[i]))// 将数据丢入解析器
        {
            unsigned char packLen = gCodecApp.RxPackBuffer[2] - 2; // 数据段长度
            unsigned char dev = gCodecApp.RxPackBuffer[4];         // 报文发送给谁
            unsigned char cmd = gCodecApp.RxPackBuffer[5];         // 指令
            unsigned char code = gCodecApp.RxPackBuffer[6];        // 附加功能码
            unsigned char *pData = &gCodecApp.RxPackBuffer[7];     // 数据段
            //            if(dev == DEV_APP)// 只处理发送给APP的数据
            //            {
            if(cmd == CMD_READ) // 读数据返回
            {
                //                    u_int32_t temp = *(u_int32_t *)pData;
                memcpy(&gDataMap[code],pData,packLen);  // 缓存收到的数据，应用层通过gDataMap素组获取最新数值。
                // 此处添加代码：发送消息给UI线程或其他逻辑处理
                //                    IMLog(@"数组 ---%d",gDataMap[code]);
                
                Byte *bytes = (Byte *) malloc(packLen);
                memcpy(bytes,pData,packLen);
                NSData *data = [[NSData alloc] initWithBytes:bytes length:length];
                
                resultBlock(data,code);
            }else{
                memcpy(&gDataMap[code],pData,packLen);// 缓存收到的数据，应用层通过gDataMap素组获取最新数值。
                // 此处添加代码：发送消息给UI线程或其他逻辑处理
                //                    IMLog(@"222数组 ---%d--%d",gDataMap[code],code);
                
                Byte *bytes = (Byte *) malloc(packLen);
                memcpy(bytes,pData,packLen);
                NSData *data = [[NSData alloc] initWithBytes:bytes length:length];
                
                resultBlock(data,code);
            }
            //            }
        }
    }
    
}

/**
 IAP解包数据
 
 data：数据段
 cmdID：命令编码
 otherID：推展功能码
 dev：发给谁
 @param data 要解包的数据
 */
+ (void)IAPUnpackWithData:(NSData *)data resultBlock:(void (^)(NSData *data ,unsigned char cmdID ,unsigned char otherID ,unsigned char dev))resultBlock
{
    NSUInteger length = [data length];
    Byte *bytes = (Byte *) malloc(length);
    [data getBytes:bytes length:length];
    
    for(int i=0; i < length; i++)
    {
        if(SeqCommPack(&gCodecApp,bytes[i]))// 将数据丢入解析器
        {
            unsigned char packLen = gCodecApp.RxPackBuffer[2] - 2; // 数据段长度
            unsigned char dev = gCodecApp.RxPackBuffer[4];         // 报文发送给谁 - 0x0A 是将报文发给主控板，0x0B是将报文发送给蓝牙面板，0x0D是将报文发送给APP
            unsigned char cmd = gCodecApp.RxPackBuffer[5];         // 功能码
            unsigned char code = gCodecApp.RxPackBuffer[6];        // 附加功能码
            unsigned char *pData = &gCodecApp.RxPackBuffer[7];     // 数据段
         
            memcpy(&gDataMap[code],pData,packLen);
            Byte *bytes = (Byte *) malloc(packLen);
            memcpy(bytes,pData,packLen);
            /// 获取日志时数据段长度为9
            if (cmd == 5) {
                length = 9;
            }
            NSData *resultdata = [[NSData alloc] initWithBytes:bytes length:length];
            resultBlock(resultdata,cmd,code,dev);
        }
    }
    
}

/*
 将收到的数据放到组合器,组包成功返回true
 */
bool SeqCommPack(S_Codec* Codec, unsigned char newRecvData)
{
    unsigned short packCheck;
    unsigned char check_H, check_L;
    
    bool ret;
    if (Codec->RxPackHand)
    {
        if (2 == Codec->RxPackIndex)
            Codec->RxPackEnd = newRecvData + (5 + 2);
        else if (3 == Codec->RxPackIndex)
            Codec->KeyIndex = newRecvData;
        else newRecvData ^= PackCryptTab[Codec->KeyIndex++];    // 从第5个字节开始，进行解密，注意密钥索引加1
        
        if (Codec->RxPackIndex < sizeof(Codec->RxPackBuffer))
        {
            Codec->RxPackBuffer[Codec->RxPackIndex] = newRecvData;
            Codec->RxPackCheck += newRecvData;
            Codec->RxPackIndex++;
            
            if (Codec->RxPackIndex >= Codec->RxPackEnd && Codec->RxPackEnd > 4)
            {
                check_H = Codec->RxPackBuffer[Codec->RxPackIndex - 1];
                check_L = Codec->RxPackBuffer[Codec->RxPackIndex - 2];
                packCheck = check_H;
                packCheck <<= 8;
                packCheck |= check_L;
                
                Codec->RxPackCheck -= Codec->RxPackBuffer[Codec->RxPackIndex - 2];
                Codec->RxPackCheck -= Codec->RxPackBuffer[Codec->RxPackIndex - 1];
                Codec->RxPackCheck ^= 0xFFFF;
                
                if (Codec->RxPackCheck == packCheck)   // 校验正确
                {
                    Codec->RcvOkPack++;
                    ret = true;
                }
                else
                {
                    Codec->RcvErrPack++;
                    ret = false;
                }
                
                Codec->RxPackIndex = 0;
                Codec->RxPackHand = false;
                Codec->RxPackHandMaybe = false;
                return ret;
            }
        }
        else // 防止溢出
        {
            Codec->RxPackIndex = 0;
            Codec->RxPackHand = false;
            Codec->RxPackHandMaybe = false;
        }
    }
    else
    {
        if (newRecvData == 0x55)
        {
            Codec->RxPackHandMaybe = true;
        }
        else
        {
            if ((newRecvData == 0xAB) && (Codec->RxPackHandMaybe))
            {
                Codec->RxPackBuffer[0] = 0x55;
                Codec->RxPackBuffer[1] = 0xAB;
                Codec->RxPackHand = true;
                Codec->RxPackIndex = 2;
                Codec->RxPackCheck = 0;
            }
            Codec->RxPackHandMaybe = false;
        }
    }
    gRandCryptKey++;
    return false;
}



@end
